package com.study.cloud.gateway.core.filter.route;

import com.study.cloud.gateway.common.constants.FilterConst;
import com.study.cloud.gateway.common.enums.ResponseCode;
import com.study.cloud.gateway.common.exception.ConnectException;
import com.study.cloud.gateway.common.exception.ResponseException;
import com.study.cloud.gateway.common.response.GatewayResponse;
import com.study.cloud.gateway.common.rule.Rule;
import com.study.cloud.gateway.core.config.ConfigLoader;
import com.study.cloud.gateway.core.context.GatewayContext;
import com.study.cloud.gateway.core.filter.Filter;
import com.study.cloud.gateway.core.filter.FilterInterface;
import com.study.cloud.gateway.core.helper.AsyncHttpHelper;
import com.study.cloud.gateway.core.helper.ResponseHelper;
import lombok.extern.slf4j.Slf4j;
import org.asynchttpclient.Request;
import org.asynchttpclient.Response;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;

/**
 * @author wangwancheng
 * @create 2025/7/20 18:54
 */
@Slf4j
@Filter(
        id = FilterConst.ROUTER_FILTER_ID,
        name = FilterConst.ROUTER_FILTER_NAME,
        order = FilterConst.ROUTER_FILTER_ORDER
)
public class RouterFilter implements FilterInterface {
    @Override
    public void doFilter(GatewayContext context) throws Exception {
        Request request = context.getRequest().build();
        CompletableFuture<Response> future = AsyncHttpHelper.getInstance().executeRequest(request);
        Boolean whenComplete = ConfigLoader.getConfig().isWhenComplete();

        if (whenComplete){
            //单异步
            future.whenComplete((response, throwable) -> {
                complete(request,response,throwable,context);
            });
        }else{
            //双异步
            future.whenCompleteAsync((response, throwable) -> {
                complete(request,response,throwable,context);
            });;
        }
    }



    private void complete(Request request, Response response, Throwable throwable, GatewayContext gatewayContext) {
        gatewayContext.releaseRequest();
        //已经重试的次数
        int currentRetryTimes = gatewayContext.getCurrentRetryTimes();
        Rule rule = gatewayContext.getRule();
        Rule.RetryConfig retryConfig = rule.getRetryConfig();
        int retryTimes = 1;
        if (retryConfig != null){
            retryTimes = retryConfig.getTimes();
        }
        if ((throwable instanceof TimeoutException
                || throwable instanceof IOException)
                && currentRetryTimes < retryTimes
        ){
            doRetry(gatewayContext, currentRetryTimes);
            return;
        }
        try {
            if (Objects.nonNull(throwable)){
                String url = request.getUrl();
                if (throwable instanceof TimeoutException){
                    //超时异常
                    log.warn("请求超时:{}",url);
                    gatewayContext.setThrowable(new ResponseException(ResponseCode.REQUEST_TIMEOUT));
                    gatewayContext.setResponse(GatewayResponse.build(ResponseCode.REQUEST_TIMEOUT));
                }else{
                    gatewayContext.setThrowable(new ConnectException(throwable,gatewayContext.getUniqueId(),
                            url,ResponseCode.HTTP_RESPONSE_ERROR));
                    gatewayContext.setResponse(GatewayResponse.build(ResponseCode.HTTP_RESPONSE_ERROR));
                }
            }else{
                gatewayContext.setResponse(GatewayResponse.build(response));
            }
        }catch (Exception e){
            gatewayContext.setThrowable(new ResponseException(ResponseCode.INTERNAL_ERROR));
            log.error("请求处理异常",e);
        }finally {
            //改变context状态
            gatewayContext.written();
            //写回我们的数据
            ResponseHelper.writeResponse(gatewayContext);
        }

    }

    private void doRetry(GatewayContext gatewayContext, int currentRetryTimes) {
        log.info("开始重试,当前重试次数:{}", currentRetryTimes);
        gatewayContext.setCurrentRetryTimes(currentRetryTimes+1);
        try {
            doFilter(gatewayContext);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
